//
// @ Mongo API 文档
// http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html
//
//

const _ = require('lodash');
const moment = require('moment');
const fp = require('fastify-plugin');
const SystemStatus = include('/modules/system_status');

const getUser = function(fastify) {
  // FIXME 这里没做区分，以后作区分要提供统一的接口

  if (SystemStatus.isIM()) {
    const businessDb = _.get(fastify, 'mongo.business.db');
    return include('/models/users')(businessDb);
  } else if (SystemStatus.isPaopai()) {
    return {
      findOneUser: async function(userId) {
        return fastify.mysql.query('select id, created_at from users where id=?', [userId]);
      },
      allRegisterUsers: async function() {
        const [counts, field] = await fastify.mysql.query('select count(*) as total_users from users');
        return _.get(counts, '[0].total_users', 0);
      },
    };
  }
};

module.exports = fp(
  function(fastify, opts, next) {
    const statisticsDB = _.get(fastify, 'mongo.statistics.db');
    const { date_format } = _.get(fastify, 'utils.constants');
    const ActionHelper = _.get(fastify, 'utils.ActionHelper');

    const users = getUser(fastify);
    const statistics_actions = include('/models/statistics_action')(statisticsDB);
    const statistics_daily = include('/models/statistics_daily')(statisticsDB);

    const arena_users = include('/models/statistics_arena_users')(statisticsDB);
    const arena_rooms = include('/models/statistics_arena_rooms')(statisticsDB);
    const arena_seasons = include('/models/statistics_arena_seasons')(statisticsDB);

    const getDistinctUsers = async function(selectedDate, action, days) {
      return await statistics_actions.distinctUsers(
        {
          max: moment(selectedDate)
            .add(1, 'day')
            .valueOf(),
          min: moment(selectedDate)
            .subtract(days, 'day')
            .valueOf(),
        },
        action
      );
    };
    const getAnalysisPaopaiActions = async function(date) {
      const statistics10Minutes = include('/models/statistics_10_minutes')(statisticsDB);

      console.log(`统计信息 ${date}, 每十分钟一次`);
      if (!moment(date).isValid()) {
        return false;
      }

      try {
        const selectedDate = moment(date).format(date_format);
        const actions = await statistics_actions.findStatisticsAction({ date: selectedDate });
        const get_daily = async function(days) {
          const time = moment(selectedDate)
            .subtract(days, 'day')
            .format(date_format);
          return await statistics_daily.findOne(time);
        };

        const todayDaily = await get_daily(0);
        const yesterdayDaily = await get_daily(1);
        const sevenDaysAgoDaily = await get_daily(6);
        const thirtyDaysAgoDaily = await get_daily(2);

        //
        // --- START --- 统计 selectedDate 日期的数据
        //

        console.log('统计数据', {
          ['今天']: todayDaily,
          ['昨天']: yesterdayDaily,
          ['3天前']: thirtyDaysAgoDaily,
          ['7天前']: sevenDaysAgoDaily,
        });

        const total_users = await users.allRegisterUsers(); // 累计注册用户数
        const online_users = _.chain(actions)
          .filter(({ action, server, timestamp }) => {
            if (ActionHelper.isOnlineUsers(action) && timestamp) {
              const tenMinutesAgo = moment().subtract(10, 'minutes');
              return moment(timestamp).isAfter(tenMinutesAgo);
            }
            return false;
          })
          .uniqBy('server')
          .sumBy(a => _.get(a, 'online_users', 0))
          .value(); // 在线人数

        const new_users = _.chain(actions)
          .filter(({ action }) => ActionHelper.isRegister(action))
          .uniqBy('userId')
          .size()
          .value(); // 新增用户
        let login_users = _.chain(actions)
          .filter(({ action }) => ActionHelper.isOpenApp(action) || ActionHelper.isRegister(action))
          .uniqBy('userId')
          .size()
          .value(); // 登录用户
        if (login_users < online_users) {
          login_users = online_users;
        }
        const played_users = _.chain(actions)
          .filter(({ action }) => ActionHelper.isPlayGame(action))
          .uniqBy('userId')
          .size()
          .value(); // 玩游戏的用户
        const cost_cards = _.chain(actions)
          .filter(({ action }) => ActionHelper.isCostCards(action))
          .sumBy(a => _.get(a, 'cost_cards', 0))
          .value(); // 消耗的房卡
        const total_cost_cards = cost_cards + _.get(yesterdayDaily, 'total_cost_cards', 0); // 累计消耗卡数

        const getRetentionUsers = function(actions, selectedDate, days) {
          return _.chain(actions)
            .filter(({ action, userCreatedAt }) => {
              if (userCreatedAt && ActionHelper.isOpenApp(action)) {
                const createdAt = moment(userCreatedAt);
                const openedAtADay = moment(selectedDate).subtract(days, 'day');
                return createdAt.format(date_format) == openedAtADay.format(date_format);
              }
              return false;
            })
            .uniqBy('userId')
            .size()
            .value(); // 次留数
        };
        const getRetentionRate = function(retention_users, daily) {
          const new_users = _.get(daily, 'new_users', 1) || 1;
          return retention_users / new_users; // 次留率
        };
        const retention_users_in_2days = getRetentionUsers(actions, selectedDate, 1); // 次留数
        const retention_users_rate_in_2days = getRetentionRate(retention_users_in_2days, yesterdayDaily); // 次留率
        const retention_users_in_3days = getRetentionUsers(actions, selectedDate, 2); // 3 日留数
        const retention_users_rate_in_3days = getRetentionRate(retention_users_in_3days, thirtyDaysAgoDaily); // 3 日留率
        const retention_users_in_7days = getRetentionUsers(actions, selectedDate, 6); // 7 日留数
        const retention_users_rate_in_7days = getRetentionRate(retention_users_in_7days, sevenDaysAgoDaily); // 7 日留率

        console.log('次留数与次留率', {
          retention_users_in_2days,
          retention_users_in_3days,
          retention_users_in_7days,
          retention_users_rate_in_2days,
          retention_users_rate_in_3days,
          retention_users_rate_in_7days,
        });

        const private_room_type = '对战卡场';
        const private_room_actions = _.chain(actions)
          .filter(
            ({ action, room_type_name }) => ActionHelper.isGameOver(action) && room_type_name == private_room_type
          )
          .value();

        const private_room_rounds = _.chain(private_room_actions)
          .sumBy(a => _.get(a, 'total_round', 0))
          .value(); // 对战卡场

        const rate_infos = await arena_users.findPlayedRounds(selectedDate);
        const rate_info = _.find(rate_infos, { _id: selectedDate });

        const arena_played_users = await arena_users.findPlayedUsers(date);

        const private_room_users = _.chain(private_room_actions)
          .map(({ uids }) => uids)
          .flatten()
          .uniq()
          .size()
          .value();

        const updatedAt = moment().format();
        const data_on_selectedDate = {
          date: selectedDate,
          total_users,
          total_cost_cards,
          new_users,
          online_users,
          login_users,
          played_users,
          cost_cards,

          retention_users_in_2days,
          retention_users_rate_in_2days,
          retention_users_in_3days,
          retention_users_rate_in_3days,
          retention_users_in_7days,
          retention_users_rate_in_7days,

          rate_room_users: arena_played_users.length,
          rate_room_rounds: _.get(rate_info, 'played_rounds', 0),
          private_room_users,
          private_room_rounds,

          updatedAt,
        };

        let daily_result;
        const previous_online_users = _.get(todayDaily, 'online_users', 0);
        const daily_set = { ...data_on_selectedDate, online_users: _.max([online_users, previous_online_users]) };
        daily_result = await statistics_daily.updateStatisticsDaily(
          { date: selectedDate },
          { $set: daily_set, $setOnInsert: { createdAt: updatedAt } }
        );

        await statistics10Minutes.insertOne({ ...data_on_selectedDate, createdAt: updatedAt });
        if (_.get(daily_result, 'result.ok') !== 1) {
          console.log('写入 daily action 出错', daily_result.result);
          return;
        }
        console.log(`统计信息 ${date}, 每十分钟一次 at ${updatedAt} 【成功】`);
        console.log(`写入的数据 ${JSON.stringify(data_on_selectedDate)}`);
      } catch (error) {
        console.log('\n\n\nerror', error);
        console.log('运行定时任务写入每日统计数据出错', error);
      }
    };

    const getAnalysisPaopaiArenaSeason = async function(date) {
      // 1. 查找当天的房间信息 - 并计算在房间玩的玩家人数
      const rooms = await arena_rooms.findSeasonRooms(date);
      const unique_rooms = _.uniqBy(rooms, 'season_id');
      console.log('rooms', rooms, unique_rooms);
      const now = new Date();

      let today_played_users = await arena_users.findPlayedUsers(date);
      for (let room of rooms) {
        const { season_id, arena_room_id } = room;
        const room_played_users = await arena_users.findPlayedUsersInSeasonRoom(season_id, arena_room_id, date);
        console.log('竞技场赛季房间 参与人数', season_id, arena_room_id, date, room_played_users.length);
        let played_users = room_played_users.length;
        await arena_rooms.updateArenaRoom(
          { season_id, arena_room_id, date },
          {
            $set: { played_users: played_users, updated_at: now },
          }
        );
      }

      for (let room of unique_rooms) {
        const { season_id } = room;
        const season_balances = await arena_rooms.countSeasonBalance(season_id);
        const total_played_users = await arena_users.findPlayedUsersInSeason(season_id);
        const season_balance = _.find(season_balances, { _id: season_id });
        console.log('竞技场赛季总计 season', season_balance, total_played_users.length);
        await arena_seasons.updateArenaSeason(
          { season_id, date },
          {
            $set: {
              ..._.omit(season_balance, '_id'),
              total_played_users: total_played_users.length,
              today_played_users: today_played_users.length,
              updated_at: now,
            },
          }
        );
      }

      // 2. 找出赛季总参与人数
      // 3. 统计赛季总收支
    };

    const getAnalysisIMActions = async function(date) {
      console.log(`统计信息 ${date}, 每十分钟一次`);
      if (!moment(date).isValid()) {
        return false;
      }
      try {
        const selectedDate = moment(date).format(date_format);
        const actions = await statistics_actions.findStatisticsAction({ date: selectedDate });

        const get_daily = function(days) {
          const time = moment(selectedDate)
            .subtract(days, 'day')
            .format(date_format);

          return statistics_daily.findOne(time);
        };

        const yesterdayDaily = await get_daily(1);
        const sevenDaysAgoDaily = await get_daily(7);
        const thirtyDaysAgoDaily = await get_daily(30);

        //
        // --- START --- 统计 selectedDate 日期的数据
        //
        const total_users = await users.allRegisterUsers(); // 累计注册用户数
        const online_users = await users.allOnlineUsers(); // 当前在线用户数
        const total_recharge_money = 0; // TODO 累计充值金额
        const total_withdraw_money = 0; // TODO 累计提现金额
        const new_registered_users = _.chain(actions)
          .filter(({ action }) => ActionHelper.isRegister(action))
          .uniqBy('userId')
          .size()
          .value(); // 新增用户

        const getRetentionUsers = function(actions, selectedDate, days) {
          return _.chain(actions)
            .filter(({ action, userCreatedAt }) => {
              if (userCreatedAt && ActionHelper.isOpenApp(action)) {
                const createdAt = moment(userCreatedAt);
                const openedAtADay = moment(selectedDate).subtract(days, 'day');
                return createdAt.format(date_format) == openedAtADay.format(date_format);
              }
              return false;
            })
            .uniqBy('userId')
            .size()
            .value(); // 次留数
        };
        const getRetentionRate = function(retention_users, daily) {
          return retention_users / _.get(daily, 'new_registered_users', 1); // 次留率
        };
        const retention_users_in_2days = getRetentionUsers(actions, selectedDate, 1); // 次留数

        const retention_users_rate_in_2days = getRetentionRate(retention_users_in_2days, yesterdayDaily); // 次留率

        const retention_users_in_7days = getRetentionUsers(actions, selectedDate, 7); // 7 日留数
        const retention_users_rate_in_7days = getRetentionRate(retention_users_in_7days, sevenDaysAgoDaily); // 7 日留率

        const retention_users_in_30days = getRetentionUsers(actions, selectedDate, 30); // 30 日留数
        const retention_users_rate_in_30days = getRetentionRate(retention_users_in_30days, thirtyDaysAgoDaily); // 30 日留率

        const getActiveUsers = function(selectedDate, days) {
          const action = ActionHelper.getOpenAppAction();
          return getDistinctUsers(selectedDate, action, days);
        };
        const active_users_daily = (await getActiveUsers(selectedDate, 1)).length || 0; // 日活
        const active_users_weekly = (await getActiveUsers(selectedDate, 7)).length || 0; // 周活
        const active_users_monthly = (await getActiveUsers(selectedDate, 30)).length || 0; // 月活
        const open_app_times = _.chain(actions)
          .filter(({ action }) => ActionHelper.isOpenApp(action))
          .sumBy(action => action.openAppTimes || 1)
          .value(); // 启动次数

        const active_times = _.chain(actions)
          .filter(({ action }) => ActionHelper.isActiveTime(action))
          .map(({ activeTimes, activeTime }) => (activeTimes ? _.map(activeTimes, 'seconds') : activeTime))
          .flatten()
          .value();
        const active_time_per_open_app = _.sum(active_times) / (active_times.length || 1); // 单次平均使用时长

        const rechargeActions = _.filter(actions, ({ action }) => ActionHelper.isRecharge(action));
        const recharge_users_daily = _.uniqBy(rechargeActions, 'userId').length; // 日充值人数
        const recharge_amount_daily = _.sumBy(rechargeActions, 'money') || 0; // 日充值金额

        const withdrawActions = _.filter(actions, ({ action }) => ActionHelper.isWithdraw(action));
        const withdraw_users_daily = _.uniqBy(withdrawActions, 'userId').length; // 日提现人数
        const withdraw_amount_daily = _.sumBy(withdrawActions, 'money') || 0; // 日提现金额

        const getRechargeUsers = function(selectedDate, days) {
          const action = ActionHelper.getRechargeAction();
          return getDistinctUsers(selectedDate, action, days);
        };

        const getWithdrawUsers = function(selectedDate, days) {
          const action = ActionHelper.getWithdrawAction();
          return getDistinctUsers(selectedDate, action, days);
        };

        const recharge_users_monthly = getRechargeUsers(selectedDate, 30).length || 0; // 月充值人数
        const recharge_amount_monthly = _.get(yesterdayDaily, 'recharge_amount_monthly', 0) + recharge_users_daily; // 月充值金额

        const withdraw_users_monthly = getWithdrawUsers(selectedDate, 30).length || 0; // 月提现人数
        const withdraw_amount_monthly = _.get(yesterdayDaily, 'withdraw_amount_monthly', 0) + withdraw_users_daily; // 月提现金额

        const total_money = 0; // 账面金额

        //
        // --- END --- 统计 selectedDate 日期的数据
        //

        const updatedAt = moment().format();
        const data_on_selectedDate = {
          date: selectedDate,
          total_users,
          online_users,
          total_recharge_money,
          total_withdraw_money,
          new_registered_users,
          retention_users_in_2days,
          retention_users_rate_in_2days,
          retention_users_in_7days,
          retention_users_rate_in_7days,
          retention_users_in_30days,
          retention_users_rate_in_30days,
          active_users_daily,
          active_users_weekly,
          active_users_monthly,
          open_app_times,
          active_time_per_open_app,
          recharge_users_daily,
          recharge_amount_daily,
          withdraw_users_daily,
          withdraw_amount_daily,
          recharge_users_monthly,
          recharge_amount_monthly,
          withdraw_users_monthly,
          withdraw_amount_monthly,
          total_money,
          updatedAt,
        };
        let daily_result;
        daily_result = await statistics_daily.updateStatisticsDaily(
          { date: selectedDate },
          { $set: data_on_selectedDate, $setOnInsert: { createdAt: updatedAt } }
        );

        if (_.get(daily_result, 'result.ok') !== 1) {
          console.log('写入 daily action 出错', daily_result.result);
          return;
        }
        console.log(`统计信息 ${date}, 每十分钟一次 at ${updatedAt} 【成功】`);
        console.log(`写入的数据 ${JSON.stringify(data_on_selectedDate)}`);
      } catch (error) {
        console.log('\n\n\nerror', error);
        console.log('运行定时任务写入每日统计数据出错', error);
      }
    };

    const getAnalysisActions = function() {
      if (SystemStatus.isIM()) {
        return getAnalysisIMActions;
      } else if (SystemStatus.isPaopai()) {
        return getAnalysisPaopaiActions;
      }
    };

    const getAnalysisArenaSeason = function() {
      if (SystemStatus.isPaopai()) {
        return getAnalysisPaopaiArenaSeason;
      } else {
        return function() {};
      }
    };

    fastify.decorate('db', {
      updateStatisticsAction: statistics_actions.updateOne,
      insertStatisticsAction: statistics_actions.insertStatisticsAction,
      updateStatisticsDaily: statistics_daily.updateStatisticsDaily,

      findOneUser: users.findOneUser,
      findOneAndUpdateUser: users.findOneAndUpdateUser,

      analysisActions: getAnalysisActions(),
      analysisArenaSeason: getAnalysisArenaSeason(),

      // 竞技场
      arena_users,
      arena_rooms,
      arena_seasons,
    });

    next();
  },
  {
    fastify: '1.7.0',
    // decorators: {
    //   fastify: ['plugin1', 'plugin2'],
    //   reply: ['compress'],
    // },
    // dependencies: ['plugin1-name', 'plugin2-name'],
  }
);
